package minidb.record;
  
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import minidb.parse.exps.Condition;
import minidb.type.fieldinfo;
import static minidb.type.Const.*;
import minidb.buffer.*;

public class TableInfo {
	private Schema schema;
	private int recordLength;
	private String tableName;
	private int startPage;
	private int indexPage;
	private int checkPage = 0;
	private Condition con = null;
	
	public int reading = 0;
	public byte[] lock = new byte[0];
	public byte[] lockindex = new byte[0];
	public int indexRead = 0;
	public long ID = -1;
	
	public TableInfo(){
		schema = new Schema();
	}
	
	public TableInfo(Schema schema, String tableName){
		this.schema = schema;
		this.tableName = tableName;
		int length = 0;
		for (fieldinfo field : schema.values()) {
			if(field.type()==TYPE.STRING || field.type()==TYPE.CHAR)
				length++;
			length+=field.length();
		}
		this.recordLength = length;
	}
	
	public TableInfo(java.nio.ByteBuffer buf) throws IOException, ClassNotFoundException{
		int maxPosition = buf.position()+buf.getInt();
		int size = (int)buf.get();
		byte []bt = new byte[size];
		buf.get(bt);
		this.tableName = new String(bt);
		this.startPage = buf.getInt();
		this.indexPage = buf.getInt();
		this.checkPage = buf.getInt();
		this.recordLength = buf.getInt();
		schema = new Schema();
		if(checkPage != 0){
			this.readCondition(checkPage);
		}
		while(true){
			fieldinfo finfo = new fieldinfo(buf);
			this.schema.add(finfo);
			if(buf.position() >= maxPosition )
				break;
		}
	}
	
	public void addCondition(Condition con) throws IOException{
		ByteArrayOutputStream bos=new ByteArrayOutputStream();
		ObjectOutputStream oos=new ObjectOutputStream(bos);
		oos.writeObject(con);
		byte[] b = bos.toByteArray();
		java.nio.ByteBuffer tmp = java.nio.ByteBuffer.allocateDirect(1000);
		this.checkPage = dbFile.allocPage();
		Buffer buf = bufpool.pin(checkPage);
		this.con = con;
		int pos = 0;
		int pre, next;
		pre = 0;
		buf.putInt(FREE, b.length);
		while(true){
			if(pos+1000 < b.length){
				buf.putInt(PRE_PAGE, pre);
				next = dbFile.allocPage();
				buf.putInt(NEXT_PAGE, next);
				tmp.rewind();
				tmp.put(b, pos, 1000);
				buf.putByte(INT_SIZE * 4, tmp, 1000);
			}
			else{
				buf.putInt(PRE_PAGE, pre);
				buf.putInt(NEXT_PAGE, 0);
				tmp.rewind();
				tmp.put(b, pos, b.length - pos);
				buf.putByte(INT_SIZE * 4, tmp, b.length - pos);
				break;
			}
			pre = buf.pageNum();
			pos+=1000;
			bufpool.unpin(buf);
			buf = bufpool.pin(next);
		}
		bufpool.unpin(buf);
	}

	public void readCondition(int pageNum) throws IOException, ClassNotFoundException{
		if(pageNum == 0){
			this.con = null;
			return;
		}
		Buffer buf = bufpool.pin(pageNum);
		int length = buf.getInt(FREE);
		byte[] b = new byte[length];
		java.nio.ByteBuffer tmp = java.nio.ByteBuffer.allocateDirect(1000);
		int pos = 0;
		int next;
		while(true){
			if(pos+1000 < length){
				buf.getByte(INT_SIZE*4, tmp, 1000);
				tmp.rewind();
				tmp.get(b, pos, 1000);
			}
			else{
				buf.getByte(INT_SIZE*4, tmp, length - pos);
				tmp.rewind();
				tmp.get(b, pos, length - pos);
				break;
			}
			next = buf.getInt(NEXT_PAGE);
			if(next == 0)
				break;
			pos +=1000;
			bufpool.unpin(buf);
			buf = bufpool.pin(next);
		}
		bufpool.unpin(buf);
		ByteArrayInputStream bis=new ByteArrayInputStream(b);
		ObjectInputStream ois=new ObjectInputStream(bis);
		this.con = (Condition)ois.readObject();
	}
	
	public void deleteCondition(){
		if(this.checkPage==0)
			return;
		Buffer buf = bufpool.pin(checkPage);
		int next = buf.getInt(NEXT_PAGE);
		this.con = null;
		this.checkPage = 0;
		while(next!=0){
			dbFile.deallocPage(buf.pageNum());
			bufpool.unpin(buf);
			buf = bufpool.pin(next);
			next = buf.getInt(NEXT_PAGE);
		}
		dbFile.deallocPage(buf.pageNum());
		bufpool.unpin(buf);
	}
	
	public Condition getCondition(){
		return this.con;
	}
	
	public String tablename(){
		return this.tableName;
	}
		
	public void tablename(String tableName){
		this.tableName = tableName;
	}
	
	public void putField(fieldinfo finfo){
		this.schema.add(finfo);
	}
	
	public void indexPage(int indexPage){
		this.indexPage = indexPage;
	}
	
	public int indexPage(){
		return this.indexPage;
	}
	
	public void checkPage(int pageNum){
		this.checkPage = pageNum;
	}
	
	public int checkPage(){
		return this.checkPage;
	}
	public fieldinfo getField(String name){
		return schema.get(name);
	}
	
	public int startpage(){
		return this.startPage;
	}
	
	public void startpage(int startPage){
		this.startPage = startPage;
	}
	
	public Schema schema(){
		return this.schema;
	}
	
	public int recordlength(){
		return this.recordLength;
	}
	
	public byte[] array(){
		int size = this.tableName.length();	
		java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(PAGE_SIZE);
		buf.position(4);
		buf.put((byte)size);
		buf.put(this.tableName.getBytes());
		buf.putInt(this.startPage);
		buf.putInt(this.indexPage);
		buf.putInt(this.checkPage);
		buf.putInt(this.recordLength);
		for(fieldinfo finfo : this.schema.values())
			buf.put(finfo.array());
		size = buf.position();
		byte []bt = new byte[size];
		buf.putInt(0, size);
		buf.rewind();
		buf.get(bt);
		return bt;
	}
}
